// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/error.dart';

/// The name and location of a diagnostic name in an ignore comment.
class DiagnosticName implements IgnoredElement {
  /// The name of the diagnostic being ignored.
  final String name;

  final int offset;

  /// Initialize a newly created diagnostic name to have the given [name] and
  /// [offset].
  DiagnosticName(this.name, this.offset);

  /// Return `true` if this diagnostic name matches the given error code.
  @override
  bool matches(ErrorCode errorCode) {
    if (name == errorCode.name.toLowerCase()) {
      return true;
    }
    var uniqueName = errorCode.uniqueName;
    var period = uniqueName.indexOf('.');
    if (period >= 0) {
      uniqueName = uniqueName.substring(period + 1);
    }
    return name == uniqueName.toLowerCase();
  }
}

class DiagnosticType implements IgnoredElement {
  final String type;

  final int offset;

  final int length;

  DiagnosticType(String type, this.offset, this.length)
      : type = type.toLowerCase();

  @override
  bool matches(ErrorCode errorCode) =>
      type == errorCode.type.name.toLowerCase();
}

abstract class IgnoredElement {
  bool matches(ErrorCode errorCode);
}

/// Information about analysis `//ignore:` and `//ignore_for_file:` comments
/// within a source file.
class IgnoreInfo {
  /// A regular expression for matching 'ignore' comments.
  ///
  /// Resulting codes may be in a list ('error_code_1,error_code2').
  static final RegExp IGNORE_MATCHER = RegExp(r'//+[ ]*ignore:');

  /// A regular expression for matching 'ignore_for_file' comments.
  ///
  /// Resulting codes may be in a list ('error_code_1,error_code2').
  static final RegExp IGNORE_FOR_FILE_MATCHER =
      RegExp(r'//[ ]*ignore_for_file:');

  /// A table mapping line numbers to the elements (diagnostics and diagnostic
  /// types) that are ignored on that line.
  final Map<int, List<IgnoredElement>> _ignoredOnLine = {};

  /// A list containing all of the elements (diagnostics and diagnostic types)
  /// that are ignored for the whole file.
  final List<IgnoredElement> _ignoredForFile = [];

  /// Initialize a newly created instance of this class to represent the ignore
  /// comments in the given compilation [unit].
  IgnoreInfo.forDart(CompilationUnit unit, String content) {

  }

  /// Return `true` if there are any ignore comments in the file.
  bool get hasIgnores =>
      _ignoredOnLine.isNotEmpty || _ignoredForFile.isNotEmpty;

  /// Return a list containing all of the diagnostics that are ignored for the
  /// whole file.
  List<IgnoredElement> get ignoredForFile => _ignoredForFile.toList();

  /// Return a table mapping line numbers to the diagnostics that are ignored on
  /// that line.
  Map<int, List<IgnoredElement>> get ignoredOnLine {
    Map<int, List<IgnoredElement>> ignoredOnLine = {};
    for (var entry in _ignoredOnLine.entries) {
      ignoredOnLine[entry.key] = entry.value.toList();
    }
    return ignoredOnLine;
  }

  /// Return `true` if the [errorCode] is ignored at the given [line].
  bool ignoredAt(ErrorCode errorCode, int line) {
    var ignoredDiagnostics = _ignoredOnLine[line];
    if (ignoredForFile.isEmpty && ignoredDiagnostics == null) {
      return false;
    }
    if (ignoredForFile.any((name) => name.matches(errorCode))) {
      return true;
    }
    if (ignoredDiagnostics == null) {
      return false;
    }
    return ignoredDiagnostics.any((name) => name.matches(errorCode));
  }
}

